﻿using System.Security.Cryptography;
using System.Text;

namespace System.NETool;

/// <summary>
/// 加密随机类，引用 "System.Security.Cryptography.RandomNumberGenerator" 实现。
/// </summary>
public class RandomEx : Random
{
    [ThreadStatic]
    private static RandomEx _globalRandom;

    /// <summary>
    /// 全局加密随机实例，用于方便地生成Nanoid。
    /// </summary>
    /// <remarks>
    /// Lazily initialized in order to account for the ThreadStatic attribute (learn more here: https://stackoverflow.com/a/18086509)
    /// </remarks>
    public new static RandomEx Shared => _globalRandom ??= new RandomEx();

    /// <summary>
    /// 返回一个非负32位随机整数。
    /// </summary>
    /// <returns>非负32位随机整数。</returns>
    public override int Next()
    {
        return Next(0, int.MaxValue);
    }

    /// <summary>
    /// 返回大于或等于0，小于最大值的32位有符号整数。
    /// </summary>
    /// <param name="maxValue">最大值</param>
    /// <returns>大于或等于0，小于最大值的32位有符号整数。</returns>
    /// <exception cref="ArgumentOutOfRangeException"></exception>
    public override int Next(int maxValue)
    {
        if (maxValue < 0) throw new ArgumentOutOfRangeException(nameof(maxValue));
        return Next(0, maxValue);
    }

    /// <summary>
    /// 返回一个大于或等于最小值，小于最大值的32位有符号整数。
    /// </summary>
    /// <param name="minValue">最小值</param>
    /// <param name="maxValue">最大值</param>
    /// <returns>大于或等于最小值，小于最大值的32位有符号整数。</returns>
    /// <exception cref="ArgumentOutOfRangeException"></exception>
    public override int Next(int minValue, int maxValue)
    {
        if (minValue > maxValue) throw new ArgumentOutOfRangeException(nameof(minValue));
        if (minValue == maxValue) return minValue;
        var range = (long)maxValue - minValue;
        return (int)((long)Math.Floor(NextDouble() * range) + minValue);
    }

    /// <summary>
    /// 获取随机数组
    /// </summary>
    /// <param name="buffer">数组</param>
    /// <exception cref="ArgumentNullException"></exception>
    public override void NextBytes(byte[] buffer)
    {
        if (buffer == null) throw new ArgumentNullException(nameof(buffer));
        RandomNumberGenerator.Fill(buffer);
    }

    /// <inheritdoc/>
    public override void NextBytes(Span<byte> buffer)
    {
        RandomNumberGenerator.Fill(buffer);
    }

    /// <summary>
    /// 返回一个大于或等于0.0且小于1.0的双精度浮点数。
    /// </summary>
    /// <returns>大于或等于0.0且小于1.0的双精度浮点数。</returns>
    public override double NextDouble()
    {
        Span<byte> uint32Buffer = stackalloc byte[4];
        RandomNumberGenerator.Fill(uint32Buffer);
        return BitConverter.ToUInt32(uint32Buffer) / (1.0 + UInt32.MaxValue);
    }

    /// <summary>
    /// 返回一个非负64位随机整数。
    /// </summary>
    /// <returns>非负64位随机整数。</returns>
    public override long NextInt64()
    {
        return NextInt64(0, long.MaxValue);
    }

    /// <summary>
    /// 返回大于或等于0，小于最大值的64位有符号整数。
    /// </summary>
    /// <param name="maxValue">最大值</param>
    /// <returns>大于或等于0，小于最大值的64位有符号整数。</returns>
    /// <exception cref="ArgumentOutOfRangeException"></exception>
    public override long NextInt64(long maxValue)
    {
        if (maxValue < 0) throw new ArgumentOutOfRangeException(nameof(maxValue));
        return NextInt64(0, maxValue);
    }

    /// <summary>
    /// 返回一个大于或等于最小值，小于最大值的64位有符号整数。
    /// </summary>
    /// <param name="minValue">最小值</param>
    /// <param name="maxValue">最大值</param>
    /// <returns>大于或等于最小值，小于最大值的64位有符号整数。</returns>
    /// <exception cref="ArgumentOutOfRangeException"></exception>
    public override long NextInt64(long minValue, long maxValue)
    {
        if (minValue > maxValue) throw new ArgumentOutOfRangeException(nameof(minValue));
        if (minValue == maxValue) return minValue;
        var range = maxValue - minValue;
        return (long)Math.Floor(NextDouble() * range) + minValue;
    }

    /// <summary>
    /// 返回一个大于或等于0.0且小于1.0的单精度浮点数。
    /// </summary>
    /// <returns>大于或等于0.0且小于1.0的单精度浮点数。</returns>
    public override float NextSingle()
    {
        return (float)NextDouble();
    }

    /// <summary>
    /// 返回指定长度的随机字节数组。
    /// </summary>
    /// <param name="length">数组长度</param>
    /// <returns>随机字节数组</returns>
    /// <exception cref="ArgumentException"></exception>
    public byte[] NextBytes(int length)
    {
        if (length < 0)
        {
            throw new ArgumentException(nameof(length));
        }

        if (length == 0) return Array.Empty<byte>();

        byte[] result = new byte[length];
        NextBytes(result);
        return result;
    }

    /// <summary>
    /// 返回指定长度的随机字节数组的Span表示。
    /// </summary>
    /// <param name="length">数组长度</param>
    /// <returns>随机字节数组的Span表示</returns>
    public Span<byte> NextSpan(int length) => NextBytes(length).AsSpan();

    /// <summary>
    /// 返回指定长度的随机汉字字符串。
    /// </summary>
    /// <param name="length">字符串长度</param>
    /// <returns>随机汉字字符串</returns>
    public string NextChinese(int length)
    {
        // 获取GB2312编码页（表）
        Encoding gb = Encoding.GetEncoding("GB2312");
        var sb = new ValueStringBuilder();
        for (int i = 0; i < length; i++)
        {
            sb.Append(gb.GetString(CreateRegionCode()));
        }

        return sb.ToString();
    }

    // 定义一个字符串数组储存汉字编码的组成元素
    private readonly string[] _base = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };

    /// <summary>
    /// 在汉字编码范围内随机创建含两个元素的十六进制字节数组，每个字节数组代表一个汉字。
    /// </summary>
    /// <returns>随机汉字的字节数组</returns>
    private byte[] CreateRegionCode()
    {
        // 每个汉字有四个区位码组成
        // 区位码第1位和区位码第2位作为字节数组第一个元素
        // 区位码第3位和区位码第4位作为字节数组第二个元素

        // 区位码第1位
        int r1 = Next(11, 14);
        string strR1 = _base[r1];
        // 区位码第2位
        int r2 = Next(0, r1 == 13 ? 7 : 16);
        string strR2 = _base[r2];
        // 区位码第3位
        int r3 = Next(10, 16);
        string strR3 = _base[r3];
        // 区位码第4位
        int r4;
        switch (r3)
        {
            case 10:
                r4 = Next(1, 16);
                break;

            case 15:
                r4 = Next(0, 15);
                break;

            default:
                r4 = Next(0, 16);
                break;
        }

        string strR4 = _base[r4];
        // 定义两个字节变量存储产生的随机汉字区位码
        byte byte1 = Convert.ToByte(strR1 + strR2, 16);
        byte byte2 = Convert.ToByte(strR3 + strR4, 16);
        // 将两个字节变量存储在字节数组中
        byte[] strR = { byte1, byte2 };
        return strR;
    }
}

